import 'package:flutter/material.dart';

/// Button disabled once it pressed and enabled again when the callback is done.
/// This is for any action which takes time and prevents duplicate tapping.
///
/// [child] displays in normal status. [disabledChild] displays once the button is pressed and action is not done yet.
///
/// When using [PressStatusButton] with method which contains callback then it will not work as you expected. Such like _userInteraction.showConfirmDialog.
/// [inProgress] is for such scenario. The button status can be controlled by this parameter.
class PressStatusButton extends StatefulWidget {
  const PressStatusButton({
    super.key,
    required this.onPressed,
    required this.child,
    this.inProgress,
    this.isTonal = false,
    this.disabledChild,
  });

  final Future Function() onPressed;
  final Widget child;
  final bool isTonal;
  final bool? inProgress;

  final Widget? disabledChild;

  @override
  State<PressStatusButton> createState() => _PressStatusButtonState();
}

class _PressStatusButtonState extends State<PressStatusButton> {
  late final Widget _disabledChild = widget.disabledChild ?? widget.child;

  // The current text for button.
  // It takes [widget.buttonText] when button is enabled.
  // It takes [widget.buttonDisabledText] when button is disabled.
  late Widget _buttonCurrentChild = widget.child;

  // Button tapping callback.
  // It takes [widget.onPressed] when button is enabled.
  // It takes null to make button disable.
  late VoidCallback? _buttonPressed = _onButtonPressed;

  @override
  Widget build(BuildContext context) {
    // If [widget.inProgress] is NOT null then we setup button status based on it.
    if (widget.inProgress != null) {
      _buttonCurrentChild =
          widget.inProgress! ? _buildDisaledButton() : widget.child;

      _buttonPressed = widget.inProgress! ? null : _onButtonPressed;
    }

    if (widget.isTonal) {
      return FilledButton.tonal(
        onPressed: _buttonPressed,
        child: _buttonCurrentChild,
      );
    }

    return FilledButton(
      onPressed: _buttonPressed,
      child: _buttonCurrentChild,
    );
  }

  Widget _buildDisaledButton() {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        const SizedBox(
          width: 15.0,
          height: 15.0,
          child: CircularProgressIndicator(strokeWidth: 2.5),
        ),
        const SizedBox(width: 10.0),
        _disabledChild,
      ],
    );
  }

  /// The [widget.onPressed] wrapper.
  Future<void> _onButtonPressed() async {
    try {
      if (mounted) {
        // Disables the button before action.
        setState(() {
          _buttonPressed = null;
          _buttonCurrentChild = _buildDisaledButton();
        });
      }

      // Calls action and waits it complete.
      // The [onPressed] might be an async method, that's why [await] needed here.
      await widget.onPressed();
    } finally {
      if (mounted && widget.inProgress == null) {
        // Restores the button when action completes.
        setState(() {
          _buttonPressed = _onButtonPressed;
          _buttonCurrentChild = widget.child;
        });
      }
    }
  }
}
